/***************************************************************************
                          expr.cpp  -  description
                             -------------------
    begin                : Tue Aug 27 2002
    copyright            : (C) 2002 by Michiel Visser
    email                : 
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "expr.h"

#ifdef DEBUGLOGGING
#define LOG_EXPR session.log(this)
#else
#define LOG_EXPR
#endif

namespace Expression
{
///.{

#define DEFPT(e) void e##Expr::printTyle() const { printf(#e); }

void Expr::evaluateToVoid()
{ Object::Holder oh;
  evaluateToCopy(oh);
}
void Expr::evaluateToCopy(void *space)
{ evaluateToOriginal()->duplicateIn(space); // assume evaluateToOriginal never returns orphan
}
Object *Expr::evaluateToOriginal() // this is the only version of this function that will create orphan
{ return evaluateToCopy();
}
void ConstExpr::evaluateToCopy(void *space) ///.
{ session.m_curTypePtr=0;
	m_val->duplicateIn(space); //todo: why this goes well?
}
void HashExpr::evaluateToCopy(void *space) { session.parseHashString(((char *)m_str)+1); new (space) NullObject();}

IFDEBUG(void HashExpr::printTyle() const { printf(m_str); })
void HashExpr::toArray(OList::P array) const
{ ADDSTR(m_str); }

///.}


void Expr::toExprArray(OList::P array) const
{
	ADDSTR("( ");
	ADDSTR("? ");
	ADDA(toArrayObject(levelModify));
	ADDSTR(" )");
}

void FuncExpr::toExprArray(OList::P array) const
{
	ADDSTR(toTyle());
}

Expr::~Expr() {}

void Expr::doStackOperation(void *space, Object *var, TypePtr s)
{ session.pushStack(var,s);
  evaluateToCopy(space);
  session.popStackAndKeep();
}

void ActAbstractExpr::evaluateToCopy(void *space)
{ EPtr e;
	Object::GC o=a->evaluateToPersistCopy();
  new (e) ActOnExpr(o->makeExpr(),b);
  new (space) ExprObject(e);
	session.m_curTypePtr=0;
}

#define DEF_T(name) void name##Expr::evaluateToCopy(void *space)
DEF_T(Negate     ) 
{ {
		Object::Holder oha;
		a->evaluateToPersistCopy(oha);
		oha->valueCopy(space);
		((Object *)space)->negate(); 
		ASSERT(session.m_keepMyChildren==0)
		session.m_keepMyChildren = (Object *)space;
	}
	session.m_keepMyChildren = 0;
	session.m_curTypePtr = 0;
}
DEF_T(Inverse    ) { Object::Holder oha; a->evaluateToCopy(oha); new (space)  IntegerObject(!oha->toInt()); session.m_curTypePtr = 0;}
DEF_T(Length     ) { Object::Holder oha; a->evaluateToCopy(oha); new (space)  IntegerObject(oha->length()); session.m_curTypePtr = 0;}
DEF_T(Type       ) { Object::Holder oha; a->evaluateToCopy(oha); new (space)  IntegerObject(oha->getTypeNum()); session.m_curTypePtr = 0;}
DEF_T(BitNot     ) 
{ { Object::Holder oha; a->evaluateToPersistCopy(oha); oha->valueCopy(space); 
		((Object *)space)->bitNot();  
		ASSERT(session.m_keepMyChildren==0)
		session.m_keepMyChildren = (Object *)space;
	}
	session.m_keepMyChildren = 0;
	session.m_curTypePtr = 0;
}
DEF_T(Describe   ) 
{ EPtr e; 
	Object::GC o=a->evaluateToPersistCopy();
	new (space)  ExprObject(o->makeExpr());
	session.m_curTypePtr = 0;
}
DEF_T(Value      ) 
{ {	Object::Holder oha; a->evaluateToCopy(oha); oha->valueCopy(space); 
		ASSERT(session.m_keepMyChildren==0);
		session.m_keepMyChildren = (Object *)space;
	}
	session.m_keepMyChildren = 0;
}
DEF_T(Reference  ) { new (space)  RefObject(a->evaluateToOriginal()); }

Object *DeReferenceExpr::evaluateToOriginal() 
{ Object *ao=a->evaluateToOriginal();
	return session.unuseObjectButKeepMember(ao,ao->deReference());
}
void DeReferenceExpr::evaluateToCopy(void *space)
{ {
		Object::Holder oa;	
		{
			a->evaluateToCopy(oa);
			Object::GC d(oa->deReference());
			d->duplicateIn(space);
			ASSERT(session.m_keepMyChildren==0)
			session.m_keepMyChildren=(Object *)oa;
		}
		session.m_keepMyChildren = (Object *)space;
	}
	session.m_keepMyChildren = 0;
}

Object *      ValueExpr::evaluateToOriginal() 
{ Object *o=a->evaluateToOriginal(), *ov=o->value(); 
  if (ov!=o) session.unuseObjectButKeepMember( o,ov );
  return ov; 
}

inline void RemoveAt(void *space, Object *a, Object *b)   { a->removeAt(b,space); }
inline void ShiftLeft(void *space, Object *a, Object *b)  { a->shiftLeft(b,space); }
inline void ShiftRight(void *space, Object *a, Object *b) { a->shiftRight(b,space); }
inline void Sub(void *space, Object *a, Object *b)        { new (space) IntegerObject(a->sub(b)?1:0); }
inline void Mul(Object *a, Object *b)        { a->mul(b); }
inline void Add(Object *a, Object *b)        { a->add(b); }
inline void Prepend(Object *a, Object *b)    { a->prepend(b);}
inline void Mod(Object *a, Object *b)
{ if (a->mod(b))
	{	ChildrenPainter cpb(b);
		session.reportRuntimeError("Modulation by zero."); 
	}
}
inline void Div(Object *a, Object *b)
{ if(!a->div(b)) 
	{	ChildrenPainter cpb(b);
		session.reportRuntimeError("Division by zero."); 
	}
}

inline void Equal(void *space, Object *a, Object *b)      { new (space) IntegerObject(*a==b?1:0); }
inline void Unequal(void *space, Object *a, Object *b)    { new (space)  IntegerObject(*a!=b?1:0); }
inline void Smaller(void *space, Object *a, Object *b)      { new (space)  IntegerObject(a->compare(b)<0); }
inline void SmallerEqual(void *space, Object *a, Object *b) { new (space)  IntegerObject(a->compare(b)<=0); }
inline void Greater(void *space, Object *a, Object *b)       { new (space)  IntegerObject(a->compare(b)>0); }
inline void GreaterEqual(void *space, Object *a, Object *b)  { new (space)  IntegerObject(a->compare(b)>=0); }
inline void BitAnd(Object *a, Object *b)  { a->bitAnd(b);}
inline void BitOr(Object *a, Object *b)   { a->bitOr(b);}
inline void BitXor(Object *a, Object *b)  { a->bitXor(b);}
#define DEF_EVAL_OF_EXEC(F) \
void F##Expr::evaluateToCopy(void *space) \
{ ChildrenPainter cp;\
	{	{	Object::Holder oha;\
			a->evaluateToCopy(oha);\
			oha->valueCopy(space);\
			cp.set((Object *)space); /*valueCopy is no excuse for not painting children*/  \
		} \
		Object::HolderCP ohb;\
		{	Object::Holder ohr;\
			b->evaluateToCopy(ohb);\
			ohb.protectChilds();\
			F(ohr,(Object *)space,ohb); \
		}\
	}\
	session.m_curTypePtr=0;\
} \
void F##AssignExpr::evaluateToCopy(void *space) \
{ {\
		Object::GC oha(a->evaluateToOriginal());\
		{	ChildrenPainter cpa(oha); \
			{	Object::HolderCP ohb;\
  			b->evaluateToCopy(ohb);\
				ohb.protectChilds();\
  			F(space, oha, ohb); \
				ASSERT(session.m_keepMyChildren==0)\
				session.m_keepMyChildren = (Object *)space;	\
			}\
		}\
	} \
	session.m_curTypePtr = 0;\
	session.m_keepMyChildren = 0;\
}

#define DEF_EVAL_OF_VOID_EXEC(F) \
void F##Expr::evaluateToCopy(void *space) \
{ ChildrenPainter cp; \
	{ {	Object::Holder oha;\
  		a->evaluateToCopy(oha);\
  		oha->valueCopy(space); \
			cp.set((Object *)space); \
		}\
		Object::HolderCP ohb; \
  	b->evaluateToCopy(ohb);\
		ohb.protectChilds();\
  	F((Object *)space,ohb); \
	} \
	session.m_curTypePtr = 0; \
} \
void F##AssignExpr::evaluateToCopy(void *space) \
{ evaluateToVoid(); new (space) NullObject(); \
}\
void F##AssignExpr::evaluateToVoid() \
{ Object::GC oha(a->evaluateToOriginal());\
	{	SelfPainter cpa(oha); \
		{	Object::HolderCP ohb;\
  		b->evaluateToCopy(ohb);\
			ohb.protectChilds();\
  		F(oha,ohb); \
		}\
	} \
}

// this one must never return childs of operands:
#define DEF_EVAL_OF_CALC(F) \
void F##Expr::evaluateToCopy(void *space) \
{ Object::HolderCP oha;\
  a->evaluateToCopy(oha);\
	{	Object::HolderCP ohb;\
		oha.protectChilds(); \
  	{	b->evaluateToCopy(ohb);\
			ohb.protectChilds();\
  		F(space, oha, ohb);\
		}\
	} \
	session.m_curTypePtr = 0; \
}

DEF_EVAL_OF_VOID_EXEC(BitAnd)
DEF_EVAL_OF_VOID_EXEC(BitOr)
DEF_EVAL_OF_VOID_EXEC(BitXor)
DEF_EVAL_OF_VOID_EXEC(Mul)
DEF_EVAL_OF_VOID_EXEC(Add)
DEF_EVAL_OF_VOID_EXEC(Prepend)
DEF_EVAL_OF_VOID_EXEC(Mod)
DEF_EVAL_OF_VOID_EXEC(Div)

DEF_EVAL_OF_EXEC(RemoveAt)
DEF_EVAL_OF_EXEC(ShiftLeft)
DEF_EVAL_OF_EXEC(ShiftRight)
DEF_EVAL_OF_EXEC(Sub)
DEF_EVAL_OF_CALC(Equal)
DEF_EVAL_OF_CALC(Unequal)
DEF_EVAL_OF_CALC(Smaller)
DEF_EVAL_OF_CALC(SmallerEqual)
DEF_EVAL_OF_CALC(Greater)
DEF_EVAL_OF_CALC(GreaterEqual)

void FindIndexExpr::evaluateToCopy(void *space)
{ {
		Object::HolderCP oha;
  	a->evaluateToCopy(oha);
		{	oha.protectChilds();
  		{	Object::HolderCP ohb;
				b->evaluateToCopy(ohb);
				ohb.protectChilds();
				oha->findIndex((Object *)ohb,space); // finished?? todo: fix problem with findindex (see implementation)
				ASSERT(session.m_keepMyChildren==0)
				session.m_keepMyChildren = (Object *)space;
			}
		}
	}
	// m_curTypePtr is already set by findIndex
	session.m_keepMyChildren = 0;
}

void InsertAtExpr::evaluateToCopy(void *space)
{ 
	ChildrenPainter cp;
  {
    {	Object::Holder oha;
    	a->evaluateToCopy(oha);
    	oha->valueCopy(space);
			cp.set((Object *)space); // prevent children of result to disappear when ohb is released. do this whenever subexpressions can be encapsulated in result
    }
		Object::HolderCP ohb;
    b->evaluateToCopy(ohb);
		ohb.protectChilds();
    ((Object *)space)->insertAt(ohb,NullObject::obj());
  }
	session.m_curTypePtr = 0;
}

Object *InsertAtAssignExpr::evaluateToOriginal() 
{ Object *r;
	SelfPainter cpr;
	{	Object::GC oha(a->evaluateToOriginal());
		TypePtr *p = session.m_curTypePtr;
		{	SelfPainter cpa(oha);
			{	Object::HolderCP ohb;
	  		b->evaluateToCopy(ohb);
				ohb.protectChilds();
	  		r=NullObject::obj();
				cpr.set(r);
	  		oha->insertAt(ohb,r);
			}
		}
		session.m_curTypePtr = p;
	}
  return r;
}

void InsertAtAssignExpr::evaluateToVoid() 
{ Object::GC oha(a->evaluateToOriginal());
	{	SelfPainter cpa(oha);
		{	Object::HolderCP ohb;
  		b->evaluateToCopy(ohb);
			ohb.protectChilds();
  		oha->insertAt(ohb,NullObject::obj());
		}
	}
}

void AndExpr::evaluateToCopy(void *space)
{ a->evaluateToCopy(space);
  if (((Object *)space)->toInt())
  {	((Object *)space)->~Object();
  	b->evaluateToCopy(space);
  }
	session.m_curTypePtr = 0;
}

void OrExpr::evaluateToCopy(void *space)
{ a->evaluateToCopy(space);
  if (!((Object *)space)->toInt())
  {	((Object *)space)->~Object();
  	b->evaluateToCopy(space);
  } 
	session.m_curTypePtr = 0;
}

void AndAssignExpr::evaluateToVoid()
{ Object::GC ao(a->evaluateToOriginal());
  if (ao->toInt() )
  { ((Object *)ao)->~Object();
	  b->evaluateToCopy(ao);
  }
}

void OrAssignExpr::evaluateToVoid()
{ Object::GC ao(a->evaluateToOriginal());
  if (!ao->toInt() )
  { ((Object *)ao)->~Object();
	  b->evaluateToCopy(ao);
  } 
}

void AndAssignExpr::evaluateToCopy(void *space) { evaluateToVoid(); new (space) NullObject(); }
void OrAssignExpr::evaluateToCopy(void *space) { evaluateToVoid(); new (space) NullObject(); }
void InsertAtAssignExpr::evaluateToCopy(void *space) {	evaluateToVoid(); new (space) NullObject(); } 
void SwitchOnExpr::evaluateToCopy(void *space)
{ int i;
	{	Object::Holder oha;
  	a->evaluateToCopy(oha);
  	i=((Object *)oha)->toInt() + 1;
	}
  EPtr *o=m_choices.pnext(i);
  if (o && !!*o) (*o)->evaluateToCopy(space);//: NullObject::obj();
  else if (!!*m_choices && ( i>1 || !!m_choices.next() ) ) (*m_choices)->evaluateToCopy(space);
  else new (space) NullObject();
}

IFDEBUG( 
void SwitchOnExpr::printTyle() const { printf("SwitchOn: "); a->printTyle(); }
)

void SwitchOnExpr::toArray(OList::P array) const
{ ADDA(a->toArrayObject(levelSwitch))
  ADDSTR(" ? ")
  bool first=true;
  FOREACH(EList, m_choices, i)
  { if (first) first=false; 
  	else ADDSTR(" : ")
    if (!!*i) 
    {	ADDA( (*i)->toArrayObject(levelSwitch) )
    }
  }
}

void ListExpr::evaluateToCopy(void *space)
{ ChildrenPainter cp;
	IFDEBUGDEEP(printf("{\n");)
	{
		push();
	  OList::P resi(&(new (space) ListObject())->getEditableListQuick());
		cp.set((Object *)space);
	  FOREACH(List<Elem>, m_elements, i)
	  { IFDEBUGDEEP( printf("le:  "); i->elem->printTyle(); printf("\n"); )
			if ( i->keepResult ) { resi->prepend(i->elem->evaluateToPersistCopy()); resi++; }
	    else i->elem->evaluateToVoid();
	  }
	  pop();
	}
	IFDEBUGDEEP(printf("}\n");)
}

IFDEBUG( DEFPT(List)  )

void ListExpr::toArray(OList::P array) const
{ if (!m_elements)
	{	ADDSTR("{");
		ADDSTR("}");
	}
	else
	{
		ADDSTR("{ ");
	  bool first=true;
	  FOREACH(List<Elem>, m_elements, i)
	  { if (first) first=false; else ADDSTR(", ")
	  	ADDA(i->elem->toArrayObject());
	    if (!i->keepResult) { first=true; ADDSTR("; "); }
	  }
		if (first) ADDSTR("}") else ADDSTR(" }");
	}
}
BlockExpr::~BlockExpr() { if (!!m_queue) ((LocalVarVectorQueue &)*m_queue).erase() ; }

void SequenceExpr::evaluateToCopy(void *space)
{ 
	push();
  if (keepResult)
  { if (!!m_elements)
  	{
  		EList::Iter i(m_elements);
  		for(;;)
  		{	EList::Iter oi(i.next());
  			if (!oi) break;
				IFDEBUGDEEP( printf("void:  "); (*i)->printTyle(); printf("\n"); )
  			(*i)->evaluateToVoid();
  			i=oi;
  		}
			IFDEBUGDEEP( printf("copy:  "); (*i)->printTyle(); printf("\n"); )
      (*i)->evaluateToCopy(space);
			ASSERT(session.m_keepMyChildren==0)
			session.m_keepMyChildren = (Object *)space;			
      pop();
			session.m_keepMyChildren = 0;
      return;
  	}
  }
  else
  {	EList::Iter i(m_elements);
    FOREACH(EList, m_elements, i)
    { IFDEBUGDEEP( printf("void:  "); (*i)->printTyle(); printf("\n"); )
			(*i)->evaluateToVoid();
    }
  }
  pop();
  new (space) NullObject();
}

IFDEBUG( DEFPT(Sequence)  )
void SequenceExpr::toArray(OList::P array) const
{ if (!m_elements) ADDSTR("()")
	else
	{
		ADDSTR("( ")
	  bool first=true;
	  FOREACH(EList, m_elements, i)
	  { if (first) first=false; else ADDSTR("; ")
	    ADDA((*i)->toArrayObject())
	  }
	  if (!first && !keepResult) ADDSTR(";")
	  ADDSTR(" )")
	}
}

void WhileExpr::evaluateToCopy(void *space)
{
  OList::P resi(&(new (space) ListObject())->getEditableListQuick());
	ChildrenPainter cp((Object *)space);
  Object *bo=ao();
  while(true)
  { 
		{
			Object::Holder oha;
    	a->evaluateToCopy(oha);
    	if (oha->toInt()==0) break;
		}
    b->evaluateToCopy(bo);
    if (bo->vtable != &vtable_Null) { bo->persist(); resi->prepend(bo); bo=ao(); resi++; }
    else bo->~Object();
  }
  objectGC.free(bo);
}

void ActOnMultiExpr::evaluateToCopy(void *space)
{ 
  OList::P resi(&(new (space) ListObject())->getEditableListQuick());
	ChildrenPainter cpr((Object *)space);
	{ 
		Object::Holder oha;
	  a->evaluateToPersistCopy(oha);
		{	TypePtr strct;
			OListPtr lp(oha->getIterList(strct));
			{	OListChildrenPainter ocp(*lp);
				Object *bo=ao();
				FOREACH(OList, *lp, i)
				{ {
						b->doStackOperation(bo,*i,strct);
				    if (bo->vtable != &vtable_Null) { bo->persist(); resi->prepend(bo); bo=ao(); resi++; }
				    else bo->~Object();
					}
				}			  
		    objectGC.free(bo);
			}
			ASSERT(session.m_keepMyChildren==0)
			session.m_keepMyChildren = (Object *)oha; // because iterlist from map contains other elements than map			
		}
		session.m_keepMyChildren =0;
	}
}

void ActOnColumnsExpr::evaluateToCopy(void *space)
{
  OList::P resi(&(new (space) ListObject())->getEditableListQuick());
	ChildrenPainter cpr((Object *)space);
	{ 
		Object::HolderCP oha;
	  a->evaluateToPersistCopy(oha);
		oha.protectChilds();
	  {
		  oha->actOnColumns(resi,b);
		}
	}
	session.m_curTypePtr=0;
}

void IndexQueryExpr::evaluateToCopy(void *space)
{ 
  OList::P resi(&(new (space) ListObject())->getEditableListQuick());
	{
		Object::Holder oha;
	  a->evaluateToPersistCopy(oha);
		{	int index=0;
			TypePtr strct;
	  	OListPtr lp(oha->getIterList(strct));
			{	OListChildrenPainter ocp(*lp);
				FOREACH(OList, *lp, i)
				{ Object::Holder ohb;
		      b->doStackOperation(ohb,*i,strct);
				   if (ohb->toInt()!=0) { resi->prepend(NEW_OBJ IntegerObject(index)); resi++; }
		      index++;
				}
			}
			ASSERT(session.m_keepMyChildren==0)
			session.m_keepMyChildren = (Object *)oha; // because iterlist from map contains other elements than map			
		}
		session.m_keepMyChildren =0;
	}
	session.m_curTypePtr=0;
}

void ApplyExpr::evaluateToCopy(void *space)
{
	{
	  Object::HolderCP ohb;
	  b->evaluateToCopy(ohb);
		TypePtr *oldType = session.m_curTypePtr;
		{	ohb.protectChilds();
			ohb->apply(a,space);
		}
		session.m_curTypePtr = oldType;
	}
  session.m_keepMyChildren=0;
}

void ApplyExpr::evaluateToVoid()
{
  Object::HolderCP ohb;
  b->evaluateToCopy(ohb);
	{	ohb.protectChilds();
		ohb->applyToVoid(a);
	}
}

void AssignmentExpr::evaluateToCopy(void *space)
{ {
		Object::GC ar(a->evaluateToOriginal());
		TypePtr *p=session.m_curTypePtr;
		{	SelfPainter arp(ar);
	    {	Object::HolderCP ohb;
		    b->evaluateToPersistCopy(ohb);
				ohb.protectChilds(); // really necessary?
		    ar->set(ohb);
			}
		  ar->duplicateIn(space);
			ASSERT(session.m_keepMyChildren==0)
	    session.m_keepMyChildren = (Object *)space; // prevent children of result to disappear when ohb is released. do this whenever subexpressions can be encapsulated in result
	    session.m_curTypePtr = p;
		}
  }
  session.m_keepMyChildren=0;
}

void AssignmentExpr::evaluateToVoid()
{ 
	Object::GC ar(a->evaluateToOriginal());
	{ SelfPainter arp(ar);
		{	Object::HolderCP ohb;
  		b->evaluateToPersistCopy(ohb);
			ohb.protectChilds(); // really necessary?
  		ar->set(ohb);
		}
  }
}

void ActOnExpr::evaluateToCopy(void *space)
{ 
  {
    Object::GC ao(a->evaluateToPersistCopy());
		{	SelfPainter spa(ao);
    	b->doStackOperation(space, ao, session.getCurType() );
			ASSERT(session.m_keepMyChildren==0)
    	session.m_keepMyChildren = (Object *)space; // prevent children of result to disappear when ohb is released. do this whenever subexpressions can be encapsulated in result
		}
  }
  session.m_keepMyChildren=0;
}

String Expr::toTyle() const
{ 
	return Object::Own(toArrayObject())->toString();
}
IFDEBUG( DEFPT() )
void Expr::toArray(OList::P array) const {}
Object *Expr::toArrayObject() const
{	OList ol;
	toArray(&ol);
	return NEW_OBJ ListObject(ol);
}
Object *Expr::toArrayObject(int parentlevel) const
{ OList ol;
	toArray(&ol);
	Object *o=NEW_OBJ ListObject(ol);
	if (level()>parentlevel)
	{
		OList pol;
		pol.prepend(NEW_OBJ StringObject(" )"));
		pol.prepend(o);
		pol.prepend(NEW_OBJ StringObject("( "));
		return NEW_OBJ ListObject(pol);
	}
	return o;
}

int HeadStructIndexingExpr::level() const { return levelHeadIndex; }

IFDEBUG( DEFPT(Const) )
void ConstExpr::toArray(OList::P array) const
{ m_val->toArray(array);
}

Object *DollarBasicExpr::evaluateToOriginal()
{	StackVar &v=session.getStackVar(m_depth);
	session.m_curTypePtr = &v.m_type;
	return v.val;
}

IFDEBUG( DEFPT(DollarVar) )
void DollarVarExpr::toArray(OList::P array) const
{	char s[3]="$"; 
  s[1]=((char *)&str)[0];
  s[2]=((char *)&str)[1];
	ADDSTR(s);
}
IFDEBUG( DEFPT(Apply) )
void ApplyExpr::toArray(OList::P array) const
{	ADDA(b->toArrayObject(levelIndex))
	if ( a->level()!=levelBlock )
	{	OList ol;
		ol.prepend(NEW_OBJ StringObject(" )"));
		ol.prepend(a->toArrayObject());
		ol.prepend(NEW_OBJ StringObject("( "));
		ADDA(NEW_OBJ ListObject(ol));
	}
	else
		ADDA(a->toArrayObject())
}

IFDEBUG( DEFPT(StructName) )
void StructNameExpr::toArray(OList::P array) const
{	ADDA(a->toArrayObject(levelHeadIndex))
	ADDSTR("[]");
}
void StructNameExpr::evaluateToCopy(void *space)
{
	{
		Object::Holder oh;
		a->evaluateToCopy(oh); // not to void, because void does keep type
	}
  TypePtr p(session.getCurType());
  if (!p) new (space) NullObject();
  else new (space) StringObject(p->m_name);
}
IFDEBUG( DEFPT(StructElems) )
void StructElemsExpr::toArray(OList::P array) const
{	ADDA(a->toArrayObject(levelHeadIndex))
	ADDSTR(".*");
}
void StructElemsExpr::evaluateToCopy(void *space)
{
	{
		Object::Holder oh;
		a->evaluateToCopy(oh); // not to void, because void does keep type
	}
  TypePtr p(session.getCurType());
  if (!p) new (space) NullObject();
  else
  { OList l;
    p->getElemNames(l);
    new (space) ListObject(l);
  }
}

IFDEBUG( DEFPT(Eval) )
void EvalExpr::toArray(OList::P array) const
{	ADDA(a->toArrayObject(levelHeadIndex))
	ADDSTR(" !");
}
void EvalExpr::evaluateToCopy(void *space)
{
  TypePtr *t;
  {
    Object::Holder oha;
    a->evaluateToCopy(oha);
	t=session.m_curTypePtr;
    oha->evaluateToCopy(space);
		ASSERT(session.m_keepMyChildren==0)
    session.m_keepMyChildren = (Object *)space; // prevent children of result to disappear when oha is released. do this whenever subexpressions can be encapsulated in result
  }
  session.m_keepMyChildren=0;
  session.m_curTypePtr=t;
}
void EvalExpr::evaluateToVoid()
{
  Object::Holder oha;
  a->evaluateToCopy(oha);
  oha->evaluateToVoid();
}

void IndexExpr::evaluateToCopy(void *space)
{	
	TypePtr *t;
  { Object::HolderCP oha;
    a->evaluateToCopy(oha);
    t=session.m_curTypePtr;
		{	oha.protectChilds();
			Object::HolderCP ohb;	    
			b->evaluateToCopy(ohb);
			ohb.protectChilds();
    	oha->indexCopy(ohb,space);
			ASSERT(session.m_keepMyChildren==0)
  		session.m_keepMyChildren = (Object *)space; // prevent children of result to disappear when holder(s) is released. do this whenever subexpressions can be encapsulated in result
		}
  }
  session.m_keepMyChildren=0;
  if (t)
  {	if (!!*t)
	  	session.m_curTypePtr = &(*t)->m_childType;
	  else
	  	session.m_curTypePtr = 0;
  }
}

void IndexAltExpr::evaluateToCopy(void *space)
{	
	{	Object::HolderCP oha;
		a->evaluateToCopy(oha);
		TypePtr *t=session.m_curTypePtr;
		{	oha.protectChilds();
			Object::HolderCP ohb;	    
			b->evaluateToCopy(ohb);
			ohb.protectChilds();
			Object *_this=oha;
			while(_this->vtable == &vtable_Ref) _this=M_OBJ;
			int tn=_this->getTypeNum();
	  	session.m_curTypePtr = 0;
			if ( tn == TOT_Map)
			{ if (t && !!*t)
  			{	session.m_curTypePtr = (*t)->getKeyPtr();
			  }
				int i=ohb->toInt();
				if (i>=0)
			  {	FOREACHM(O_MAP->, { if (!i) { p->key()->duplicateIn(space); goto done; }; i--;} )
				}
				else
			  {	FOREACHM_BACK(O_MAP->, { i++; if (!i) { p->key()->duplicateIn(space); goto done; }; } )
				}
			}
			else if ( tn == TOT_List)
			{ int idx=0;
				FOREACH(OList, O_LIST->getList(), i)
				{	if (**i==ohb)
					{	new (space) IntegerObject(idx);
						goto done;
					}
					idx++;
				}
			}
			else if ( tn == TOT_String)
			{	Object *b = ohb->value();
				String as=_this->toString();
				if (b ->getTypeNum() == TOT_RegExp )
				{		List<RE::Match> matches;
						((RegExpObject *)b)->m_expr->match(&matches,as,as.length(),true);
						t = session.m_curTypePtr = 0;
						if (!!matches)
						{
							OList m;
							{	OList regs;
								OList::P regsi(&regs);
								FOREACH(List<String>, matches->m_regs,j)
								{	regsi->prepend(NEW_OBJ StringObject(*j));
									regsi++;
								}
								m.prepend(NEW_OBJ ListObject(regs));
							}
							m.prepend(NEW_OBJ IntegerObject(matches->m_len));
							m.prepend(NEW_OBJ IntegerObject(matches->m_pos));
							new (space) ListObject(m);
							session.m_curTypePtr=&RegExpResultType->m_childType;
							goto done;
						}
						//todo: set type
						//todo: make one function for adding regexp output item
				}		
				else
				{
					String bs=b->toString();
					char *res=strstr((char *)as,(char *)bs);
					if (res)
					{	new (space) IntegerObject(res-as);
						goto done;
					}
				}
			}
			new (space) NullObject(); 
done:
			ASSERT(session.m_keepMyChildren==0)
  		session.m_keepMyChildren = (Object *)space; // prevent children of result to disappear when holder(s) is released. do this whenever subexpressions can be encapsulated in result
		}
  }
  session.m_keepMyChildren=0;
}

Object *IndexExpr::evaluateToOriginal()
{	Object *ao(a->evaluateToOriginal());
  TypePtr *t=session.m_curTypePtr;
	Object *i;
	{	SelfPainter cpa(ao);
		{	Object::HolderCP ohb;	    
		  b->evaluateToCopy(ohb);
			ohb.protectChilds();
			i=ao->index(ohb);
		}
	}
  if (t)
  {	if (!!*t)
	  	session.m_curTypePtr = &(*t)->m_childType;
	  else
	  	session.m_curTypePtr = 0;
  }
  return session.unuseObjectButKeepMember(ao,i);
}

IFDEBUG( DEFPT(Index) )
void IndexExpr::toArray(OList::P array) const
{	ADDA(a->toArrayObject(levelHeadIndex))
	ADDSTR("[");
	ADDA(b->toArrayObject())
	ADDSTR("]");
}

IFDEBUG( DEFPT(IndexAlt) )
void IndexAltExpr::toArray(OList::P array) const
{	ADDA(a->toArrayObject(levelHeadIndex))
	ADDSTR("[:");
	ADDA(b->toArrayObject())
	ADDSTR("]");
}

//todo: mapnode always initialized key, maybe also put mapnodes in allocator
void MapExpr::evaluateToCopy(void *space)
{	MapObject *m=new (space) MapObject();
	{	ChildrenPainter cp((Object *)space);
	  {
			FOREACH(List<EPtr>, m_elements, i)
		  { 
		    {
		      Object::Holder oha;
		      (*i)->evaluateToPersistCopy(oha);
		      i++;
					if (!*i)
			      m->insertAt((Object *)oha, NullObject::obj());
					else
					{ ChildrenPainter cpa(oha);
		      	m->insertAt((Object *)oha, (*i)->evaluateToPersistCopy());
					}
		    }
		  }
		}
	}
}

IFDEBUG( DEFPT(Map) )
void MapExpr::toArray(OList::P array) const
{	ADDSTR("{: ");
  bool first=true;
  FOREACH(List<EPtr>, m_elements, i)
  { 
    if (first) { first=false; } else { ADDSTR(", "); }
    OList pair;
    pair.prepend((*i)->toArrayObject());
    i++;
    if (!!*i)
    { pair.end().prepend(NEW_OBJ StringObject(" => "));
      pair.end().prepend((*i)->toArrayObject());
    }
  	ADDA(NEW_OBJ ListObject(pair));
  }
	ADDSTR(" }");
}

void BlockVarExpr::evaluateToCopy(void *space)
{	
	LOG_EXPR;
	VarValue &vec = ((LocalVarVectorQueue &)*queue).getVarValue(number);
	session.m_curTypePtr = &vec.m_type;
	vec.m_value->duplicateIn(space);
}
Object *BlockVarExpr::evaluateToOriginal()
{ 
	LOG_EXPR;
	VarValue &vec = ((LocalVarVectorQueue &)*queue).getVarValue(number);
	session.m_curTypePtr = &vec.m_type;
	return vec.m_value;
}
IFDEBUG(void BlockVarExpr::printTyle() const { printf(".%s",(char *)((LocalVarVectorQueue &)*queue).getName(number)); })
void BlockVarExpr::toArray(OList::P array) const
{	if (session.m_annotate)
		ADDSTR("/*l*/");
	ADDSTR(((LocalVarVectorQueue &)*queue).getName(number));
}
IFDEBUG(void LocalBlockVarExpr::printTyle() const { BlockVarExpr::printTyle(); })
void LocalBlockVarExpr::toArray(OList::P array) const
{	BlockVarExpr::toArray(array);
	ADDSTR(".")
}
Object *GlobalVarExpr::evaluateToOriginal()
{ 
	LOG_EXPR;
	session.m_curTypePtr = &m_v.getType();
	return m_v.m_value;
}

Object *GlobalVarInitialExpr::evaluateToOriginal()
{ 
	GlobalVariable *g=session.getGlobalVar(m_name);
	g->expr = EPtr(this);
	this->~GlobalVarInitialExpr();
	return (new (this) GlobalVarExpr(*g))->evaluateToOriginal();
}

IFDEBUG(void GlobalVarInitialExpr::printTyle() const { printf("%s",(char *)m_name); })

IFDEBUG(void GlobalVarExpr::printTyle() const { printf("%s",(char *)m_v.getName()); })
void GlobalVarExpr::toArray(OList::P array) const
{	if (session.m_annotate)
		ADDSTR("/*g*/");
	ADDSTR(m_v.getName());
}

void GlobalVarInitialExpr::toArray(OList::P array) const
{	if (session.m_annotate)
		ADDSTR("/*g*/");
	ADDSTR(m_name);
}

void StructIndexingExpr::evaluateToCopy(void *space)
{ 
  {
    Object::HolderCP oha;
    a->evaluateToCopy(oha);
		oha.protectChilds();
    if (updateIndex())
		{	oha->indexCopy(m_index.m_index,space);
		}
    else
    { new (space) NullObject();
		  session.m_curTypePtr = 0;
		}
		ASSERT(session.m_keepMyChildren==0)
    session.m_keepMyChildren = (Object *)space;  // prevent children of result to disappear when oha is released. do this whenever subexpressions can be encapsulated in result
  }
  session.m_keepMyChildren = 0;  
}

Object *StructIndexingExpr::evaluateToOriginal()
{ 
	Object *ao=a->evaluateToOriginal();
	bool u;
	{	SelfPainter p(ao);
		u=updateIndex();
	}
  if (u)
  {	return session.unuseObjectButKeepMember(ao, ao->index(m_index.m_index));
  }
  else
  { objectGC.unuse(ao);
  	session.m_curTypePtr = 0;
    return NullObject::obj();
  }
}

Object *ParamExpr::evaluateToOriginal()
{ 
	LOG_EXPR;
	Object *ao=DollarBasicExpr::evaluateToOriginal();
	bool u;
	{	SelfPainter p(ao);
		u=updateIndex();
	}
  if (u)
  { return session.unuseObjectButKeepMember(ao, ao->index(m_index.m_index));
  }
  else
  { objectGC.unuse(ao);
    session.m_curTypePtr = 0;
    return NullObject::obj();
  }
}

Object *HeadStructIndexingExpr::evaluateToOriginal() // use default evaluateToOriginal function instead the one of StructIndexingExpr
{ 
	return Expr::evaluateToCopy();
}

void HeadStructIndexingExpr::evaluateToCopy(void *space)
{ 
  {
	  Object::HolderCP oha;
	  a->evaluateToCopy(oha);
		oha.protectChilds();
		if (updateIndex())
		{ Object::HolderCP ohHead;
			oha->indexCopy(0,ohHead);
			ohHead.protectChilds();
			ohHead->indexCopy(m_index.m_index,space);
  		session.m_keepMyChildren = (Object *)space; // prevent children of result to disappear when holder(s) is released. do this whenever subexpressions can be encapsulated in result
		}
	  else
	  {
	  	new (space) NullObject();
	  	session.m_curTypePtr = 0;
  	}
	}
	session.m_keepMyChildren = 0;
}

void HeadCallExpr::evaluateToCopy(void *space)
{ // a->index(b)
  {
    Object::GC oha(a->evaluateToPersistCopy());
		TypePtr typeOfA ( session.getCurType() );
		if (updateIndex())
		{ TypePtr *resType=session.m_curTypePtr;
			Object::HolderCP ohHead;
			oha->indexCopy(0,ohHead);
			ohHead.protectChilds();
			{	Object::Holder oh; // no need to CP because parent is already CPed
				ohHead->indexCopy(m_index.m_index,oh);
				Object *ob=b->evaluateToCopy();
				session.pushStack(oha,typeOfA);
				session.pushStack(ob,session.getCurType());
				oh->evaluateToCopy(space);
				ASSERT(session.m_keepMyChildren==0)
	  		session.m_keepMyChildren = (Object *)space; // prevent children of result to disappear when holder(s) is released. do this whenever subexpressions can be encapsulated in result
				session.popStack();
			}
			session.popStackAndKeep();
			session.m_curTypePtr = resType;
		}
    else
    {	session.m_curTypePtr = 0;
      new (space) NullObject();
  	}
  }
	session.m_keepMyChildren = 0;
}

void HeadStructIndexingExpr::toArray(OList::P array) const
{ ADDA(this->a->toArrayObject(levelHeadIndex));
	ADDSTR("->");
  ADDSTR(m_index.m_name)
}

void HeadCallExpr::toArray(OList::P array) const
{ 
	HeadStructIndexingExpr::toArray(array);
	array=&array->end();
	if ( b->level()!=levelBlock )
	{	OList ol;
		ol.prepend(NEW_OBJ StringObject(" )"));
		ol.prepend(b->toArrayObject());
		ol.prepend(NEW_OBJ StringObject("( "));
		ADDA(NEW_OBJ ListObject(ol))
	}
	else
		ADDA(b->toArrayObject())
}

int Expr::level() const { return 0; }
int BlockExpr::level() const { return levelBlock; }
int ConstExpr::level() const
{ int t=m_val->getTypeNum();
	return t==TOT_Null || t==TOT_List || t==TOT_Map ? levelBlock : 0;
}

void TypeSelectionExpr::toArray(OList::P array) const
{	ADDA(bracketize(m_func->toArrayObject(m_typeFuncs)));
	if (!!a) ADDA(a->toArrayObject(levelOperand))
}
IFDEBUG( DEFPT(TypeSelection) )
IFDEBUG( 
void ParamExpr::printTyle() const { printf("%s", (char *)m_index.m_name);}
)
void ParamExpr::toArray(OList::P array) const
{	if (session.m_annotate)
		ADDSTR("/*p*/");
	ADDSTR(m_index.m_name)
}

IFDEBUG( 
void SingleParamExpr::printTyle() const 
{ printf("%s", (char *)((SingleParamScope &)*m_scope).m_name);
}
)

void SingleParamExpr::toArray(OList::P array) const
{	if (session.m_annotate)
		ADDSTR("/*p*/");
	ADDSTR(((SingleParamScope &)*m_scope).m_name);
}

IFDEBUG( 
void StructIndexingExpr::printTyle() const { a->printTyle(); printf(" . %s", (char *)m_index.m_name);}
)
void StructIndexingExpr::toArray(OList::P array) const
{	ADDA(a->toArrayObject(levelHeadIndex))
  ADDSTR(".")
  ADDSTR(m_index.m_name)
}

void StructIndexingNumberExpr::evaluateToCopy(void *space)
{ 
	a->evaluateToVoid(); //necessary because this can change index
  if (updateIndex())
  { new (space)  IntegerObject(m_index.m_index);
  }
  else
    new (space) NullObject();
}

TypeSelectCarrier::TypeSelectCarrier(List<TypeFunc> &funcs) 
{	m_typeFuncs.take(funcs);
	FOREACHP(List<TypeFunc>, &m_typeFuncs, i)
	{	(*i)->setSelfHolderPtr(i);
	}
}

void TypeSelectCarrier::assignTypeVars() const
{ 	
	FOREACH(List<TypeFunc>, m_typeFuncs, i)
  	i->evaluate();
}
Object *TypeSelectionExpr::evaluateToOriginal()
{ 
	assignTypeVars();

  if (!!a)  // assignment to variables:
  { 
    Object *res = a->evaluateToOriginal();
    session.setCurType(m_func->evaluate());
    return res;
  }
  else
  { session.m_curTypePtr = 0;
	  return NullObject::obj();
  }
}
void TypeSelectionExpr::evaluateToCopy(void *space)
{ 
	assignTypeVars();
  if (!!a) 
  { 
    a->evaluateToCopy(space);
    session.setCurType(m_func->evaluate());
  }
  else
	{	m_func->evaluate(); // can also be, and probably is, a type assignment
    new (space) NullObject();
	}
}
// no TypeSelectionExpr::evaluateToVoid(), because we cannot evaluate 'a' to void anyway

void StructLengthExpr::toArray(OList::P array) const
{
	ADDA(a->toArrayObject(levelHeadIndex))
  ADDSTR(".@")
}

void StructLengthExpr::evaluateToCopy(void *space)
{
  a->evaluateToVoid();
	TypePtr t(session.getCurType());
	new (space) IntegerObject (!!t?t->m_elems.length():0);
	session.m_curTypePtr = 0;
}

bool StructIndexingExpr::updateIndex()
{
  switch(m_index.update())
  {
  case 0: return true;
  case 1: session.reportRuntimeError(String("Cannot find element '")+m_index.m_name+"' in '"+toTyle()+"'."); return false;
  case 2: session.reportRuntimeError(String("Indexing of non-struct in '")+toTyle()+"'.");
  }
}

bool ParamExpr::updateIndex()
{
  if (m_index.update())
  {	session.reportRuntimeError(String("Cannot find parameter '")+m_index.m_name+"'."); return false;
	}
	return true;
}

IFDEBUG( DEFPT(StructIndexingNumber) )
void StructIndexingNumberExpr::toArray(OList::P array) const
{	ADDA(a->toArrayObject(levelHeadIndex))
  ADDSTR(".@")  
  ADDSTR(m_index.m_name)
}

void SingleParamAssignmentExpr::initEval() const
{
	assignTypeVars();
	if(!!m_func)
		session.getStackVar(((SingleParamScope &)*m_scope).m_depth).m_type = m_func->evaluate();
	session.execPush(m_scope);
}

void SingleParamAssignmentExpr::evaluateToCopy(void *space)
{
	initEval(); 
	a->evaluateToCopy(space);
	session.execPop();	
}
void SingleParamAssignmentExpr::evaluateToVoid()
{ 
	initEval();
	a->evaluateToVoid();
	session.execPop();	
}

IFDEBUG( 
void ParamSetAssignmentExpr::printTyle() const 
{ printf("<...,...>");
  a->printTyle();
}
)

void ParamSetAssignmentExpr::initEval() const
{
	assignTypeVars();
	TypePtr p(((ParamSetScope &)*m_scope).m_type);
	if (! p->isEmpty() )
		session.getStackVar(((ParamSetScope &)*m_scope).m_depth).m_type = p;
	session.execPush(m_scope);
}

void ParamSetAssignmentExpr::evaluateToCopy(void *space)
{ initEval();
	a->evaluateToCopy(space);
	session.execPop();
}
void ParamSetAssignmentExpr::evaluateToVoid()
{ initEval();
	a->evaluateToVoid();
	session.execPop();
}

// more sharing
void SingleParamAssignmentExpr::toArray(OList::P array) const
{	ADDSTR("< ")
	if (!!m_func)
  	ADDA(bracketize(m_func->toArrayObject(m_typeFuncs)));
	ADDSTR(((SingleParamScope &)*m_scope).m_name);
  ADDSTR(" > ");
  ADDA(a->toArrayObject(levelParam));
}
void ParamSetAssignmentExpr::toArray(OList::P array) const
{	
	{	ADDSTR("< ")
		ADDA(((ParamSetScope &)*m_scope).m_type->elemsToArrayObject(m_typeFuncs));
  	ADDSTR("> ");
	}
  ADDA(a->toArrayObject(levelParam));
}

#ifdef DEBUGLOGGING
Object *SingleParamExpr::evaluateToOriginal() 
{
	LOG_EXPR;
	return DollarBasicExpr::evaluateToOriginal();
}
#endif

IFDEBUG(void SingleParamAssignmentExpr::printTyle() const { printf("spa\n"); })

IFDEBUG( DEFPT(ClosureVar) )
Object *ClosureVarExpr::evaluateToOriginal() 
{ 
	LOG_EXPR;
	VarValue &v=session.m_curClosures->m_vars[m_index];
	session.m_curTypePtr = &v.m_type;
	return v.m_value;
}
void ClosureVarExpr::toArray(OList::P array) const
{
	if (session.m_annotate)
		ADDSTR("/*c*/");
	ADDSTR(((ClosureScope &)*session.m_curClosures->m_scope).m_closures[m_index].name)
}
IFDEBUG(void ClosuredExprExpr::printTyle() const { printf("?closure? "); a->printTyle(); } )

void ClosuredExprExpr::toArray(OList::P array) const
{
	ADDSTR("? ");
	ClosureBinding *ocs=session.m_curClosures;
	ClosureBindingPtr p;
	new (p) ClosureBinding(m_scope);
	session.m_curClosures=p;
	ADDA(a->toArrayObject(levelModify));
	session.m_curClosures = ocs;
}

void ClosuredExprExpr::evaluateToCopy(void *space) ///.
{ 
	ClosureBindingPtr p;
	new (p) ClosureBinding(a,m_scope);
	new (space) ClosuredExprObject(p );
}

} // namespace

